home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Sprite 1984 - 1993
/
Sprite 1984 - 1993.iso
/
src
/
kernel
/
proc
/
procFork.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-18
|
14KB
|
513 lines
/*
* procFork.c --
*
* Routines to create new processes. No monitor routines are required
* in this file. Synchronization to proc table entries is by a call
* to the proc table monitor to get a PCB and calls to the family monitor
* to put a newly created process into a process family.
*
* Copyright (C) 1985, 1988 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header: /cdrom/src/kernel/Cvsroot/kernel/proc/procFork.c,v 9.11 91/11/15 21:06:46 kupfer Exp $ SPRITE (Berkeley)";
#endif /* not lint */
#include <sprite.h>
#include <mach.h>
#include <list.h>
#include <proc.h>
#include <procInt.h>
#include <sched.h>
#include <status.h>
#include <stdlib.h>
#include <string.h>
#include <sync.h>
#include <sys.h>
#include <timer.h>
#include <vm.h>
#include <prof.h>
#include <procUnixStubs.h>
/*
* There is only one vfork sleep condition in the system.
* When a child does a Sync_Broadcast, all sleeping parents
* wake up, check the VFORKPARENT flag in their respective
* PCBs and all but one (hopefully) go back to sleep.
* If the contention level is too high we can put a condition lock
* in each PCB, but this requires recompiling most of the world
* so lets try the easy way first.
*/
static Sync_Condition vforkCondition;
static Sync_Lock vforkLock;
#define LOCKPTR &vforkLock
static ReturnStatus InitUserProc _ARGS_((Proc_ControlBlock *procPtr,
Proc_ControlBlock *parentProcPtr,
Boolean shareHeap, Boolean vforkFlag));
/*
*----------------------------------------------------------------------
*
* Proc_Fork --
*
* Process the fork system call.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Proc_Fork(shareHeap, pidPtr)
Boolean shareHeap;
Proc_PID *pidPtr;
{
Proc_PID *newPidPtr;
int numBytes;
ReturnStatus status;
/*
* Make the pointer to the process id that is to be returned accessible.
*/
Vm_MakeAccessible(VM_OVERWRITE_ACCESS,
sizeof(Proc_PID), (Address) pidPtr,
&numBytes, (Address *) &newPidPtr);
if (numBytes < sizeof(Proc_PID)) {
return(SYS_ARG_NOACCESS);
}
/*
* Start up the new process. The PC where to begin execution doesn't
* matter since it has already been stored in the proc table entry before
* we were called.
*/
status = Proc_NewProc((Address) 0, PROC_USER, shareHeap, newPidPtr,
(char *)NIL, FALSE);
Vm_MakeUnaccessible((Address) newPidPtr, numBytes);
return(status);
}
/*
*----------------------------------------------------------------------
*
* Proc_Vfork --
*
* Process the vfork system call.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Proc_Vfork()
{
ReturnStatus status;
Proc_PID newPid;
status = Proc_NewProc((Address) 0, PROC_USER, FALSE, &newPid,
(char *) NIL, TRUE);
if (status != SUCCESS) {
Mach_SetErrno(Compat_MapCode(status));
return -1;
}
return (int) newPid;
}
/*
* ----------------------------------------------------------------------------
*
* Proc_VforkWakeup
*
* Called by vfork'd child to wakeup waiting parent
* (when child dies or execs). Caller must hold a lock
* on the child's PCB entry (ie. must have called Proc_Lock()).
*
* Results:
* None.
*
* Side effects:
* Will make parent process runnable.
*
* ----------------------------------------------------------------------------
*/
void
Proc_VforkWakeup(procPtr)
Proc_ControlBlock *procPtr;
{
Proc_ControlBlock *parentProcPtr;
parentProcPtr = Proc_GetPCB(procPtr->parentID);
Proc_Lock(parentProcPtr);
if (!(parentProcPtr->genFlags & PROC_VFORKPARENT)) {
panic("VForkWakeup called but VFORKPARENT flag == 0.");
}
parentProcPtr->genFlags &= ~PROC_VFORKPARENT;
Proc_Unlock(parentProcPtr);
LOCK_MONITOR;
Sync_Broadcast(&vforkCondition);
UNLOCK_MONITOR;
procPtr->genFlags &= ~PROC_VFORKCHILD;
}
/*
* ----------------------------------------------------------------------------
*
* Proc_NewProc --
*
* Allocates a PCB and initializes it.
*
* Results:
* Pointer to process control block for created process.
*
* Side effects:
* PCB initialized and made runnable.
*
* ----------------------------------------------------------------------------
*/
ReturnStatus
Proc_NewProc(PC, procType, shareHeap, pidPtr, procName, vforkFlag)
Address PC; /* The program counter where to start. */
int procType; /* One of PROC_KERNEL or PROC_USER. */
Boolean shareHeap; /* TRUE if share heap, FALSE if not. */
Proc_PID *pidPtr; /* A pointer to where to return the process
ID in. */
char *procName; /* Name for process control block */
Boolean vforkFlag; /* Added for vfork */
{
ReturnStatus status;
Proc_ControlBlock *procPtr; /* The new process being created */
Proc_ControlBlock *parentProcPtr; /* The parent of the new process,
* the one that is making this call */
Boolean migrated = FALSE;
parentProcPtr = Proc_GetActualProc();
if (parentProcPtr->genFlags & PROC_FOREIGN) {
migrated = TRUE;
}
procPtr = ProcGetUnusedPCB();
if (pidPtr != (Proc_PID *) NIL) {
*pidPtr = procPtr->processID;
}
procPtr->Prof_Scale = 0;
Prof_Enable(procPtr, parentProcPtr->Prof_Buffer,
parentProcPtr->Prof_BufferSize, parentProcPtr->Prof_Offset,
parentProcPtr->Prof_Scale);
procPtr->processor = parentProcPtr->processor;
procPtr->genFlags |= procType;
if (vforkFlag) {
procPtr->genFlags |= PROC_VFORKCHILD;
}
procPtr->syncFlags = 0;
procPtr->schedFlags = 0;
procPtr->exitFlags = 0;
if (!migrated) {
procPtr->parentID = parentProcPtr->processID;
} else {
procPtr->parentID = parentProcPtr->peerProcessID;
}
procPtr->familyID = parentProcPtr->familyID;
procPtr->userID = parentProcPtr->userID;
procPtr->effectiveUserID = parentProcPtr->effectiveUserID;
procPtr->billingRate = parentProcPtr->billingRate;
procPtr->recentUsage = 0;
procPtr->weightedUsage = 0;
procPtr->unweightedUsage = 0;
procPtr->kernelCpuUsage.ticks = timer_TicksZeroSeconds;
procPtr->userCpuUsage.ticks = timer_TicksZeroSeconds;
procPtr->childKernelCpuUsage.ticks = timer_TicksZeroSeconds;
procPtr->childUserCpuUsage.ticks = timer_TicksZeroSeconds;
procPtr->numQuantumEnds = 0;
procPtr->numWaitEvents = 0;
procPtr->event = NIL;
procPtr->kcallTable = mach_NormalHandlers;
procPtr->unixProgress = parentProcPtr->unixProgress;
/*
* Free up the old argument list, if any. Note, this could be put
* in Proc_Exit, but is put here for consistency with the other
* reinitializations of control block fields.
p */
if (procPtr->argString != (Address) NIL) {
free((Address) procPtr->argString);
procPtr->argString = (Address) NIL;
}
/*
* Create the argument list for the child. If no name specified, take
* the list from the parent. If one is specified, just make a one-element
* list containing that name.
*/
if (procName != (char *)NIL) {
procPtr->argString = (char *) malloc(strlen(procName) + 1);
(void) strcpy(procPtr->argString, procName);
} else if (parentProcPtr->argString != (Address) NIL) {
procPtr->argString =
(char *) malloc(strlen(parentProcPtr->argString) + 1);
(void) strcpy(procPtr->argString, parentProcPtr->argString);
}
if (!migrated) {
if (ProcFamilyInsert(procPtr, procPtr->familyID) != SUCCESS) {
panic("Proc_NewProc: ProcFamilyInsert failed\n");
}
}
/*
* Initialize our child list to remove any old links.
* If not migrated, insert this PCB entry into the list
* of children of our parent.
*/
List_Init((List_Links *) procPtr->childList);
if (!migrated) {
List_Insert((List_Links *) &(procPtr->siblingElement),
LIST_ATREAR(parentProcPtr->childList));
}
Sig_Fork(parentProcPtr, procPtr);
Vm_ProcInit(procPtr);
/*
* If the process is migrated, setup its process state on the home node.
*/
if (migrated) {
status = ProcRemoteFork(parentProcPtr, procPtr);
if (status != SUCCESS) {
/*
* We couldn't fork on the home node, so free up the new
* process that we were in the process of allocating.
*/
Proc_Unlock(procPtr);
ProcFreePCB(procPtr);
return(status);
}
/*
* Change the returned process ID to be the process ID on the home
* node.
*/
if (pidPtr != (Proc_PID *) NIL) {
*pidPtr = procPtr->peerProcessID;
}
} else {
procPtr->peerHostID = NIL;
procPtr->peerProcessID = NIL;
}
/*
* Set up the virtual memory of the new process.
*/
if (procType == PROC_KERNEL) {
status = Mach_SetupNewState(procPtr, (Mach_State *)NIL,
Sched_StartKernProc, PC, FALSE);
if (status != SUCCESS) {
/*
* We are out of kernel stacks.
*/
Proc_Unlock(procPtr);
ProcFreePCB(procPtr);
return(status);
}
} else {
status = InitUserProc(procPtr, parentProcPtr, shareHeap, vforkFlag);
if (status != SUCCESS) {
/*
* We couldn't allocate virtual memory, so free up the new
* process that we were in the process of allocating.
*/
if (!migrated) {
ProcFamilyRemove(procPtr);
List_Remove((List_Links *) &(procPtr->siblingElement));
}
Proc_Unlock(procPtr);
ProcFreePCB(procPtr);
return(status);
}
}
/*
* Mark ourselves waiting, if necessary
*/
if (vforkFlag) {
Proc_Lock(parentProcPtr);
parentProcPtr->genFlags |= PROC_VFORKPARENT;
Proc_Unlock(parentProcPtr);
}
/*
* Set up the environment of the process.
*/
if (!migrated) {
ProcSetupEnviron(procPtr);
}
/*
* Have the new process inherit filesystem state.
*/
Fs_InheritState(parentProcPtr, procPtr);
/*
* Return PROC_CHILD_PROC to the newly created process.
*/
if (procPtr->unixProgress == PROC_PROGRESS_NOT_UNIX) {
Mach_SetReturnVal(procPtr, (vforkFlag ? 0 : (int) PROC_CHILD_PROC), 1);
} else {
Mach_SetReturnVal(procPtr, 0, 1);
}
/*
* Now that we're done messing with the PCB, unlock it. Maybe this
* could get moved up to happen earlier in the function, but there's
* probably no harm to delaying until now.
*/
Proc_Unlock(procPtr);
/*
* Put the process on the ready queue.
*/
Sched_MakeReady(procPtr);
/*
* Now make the parent wait until child exec`s or exits
*/
if (vforkFlag) {
LOCK_MONITOR;
while (parentProcPtr->genFlags & PROC_VFORKPARENT) {
Sync_Wait(&vforkCondition, FALSE);
}
UNLOCK_MONITOR;
}
return(SUCCESS);
}
/*
*----------------------------------------------------------------------
*
* InitUserProc --
*
* Initalize the state for a user process. This involves allocating
* the segments for the new process.
*
* Results:
* None.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static ReturnStatus
InitUserProc(procPtr, parentProcPtr, shareHeap, vforkFlag)
register Proc_ControlBlock *procPtr; /* PCB to initialized.*/
register Proc_ControlBlock *parentProcPtr; /* Parent's PCB. */
Boolean shareHeap; /* TRUE => share heap
* with parent. */
Boolean vforkFlag; /* TRUE => share all
* segs with parent. */
{
ReturnStatus status;
/*
* Set up a kernel stack for the process.
*/
status = Mach_SetupNewState(procPtr, parentProcPtr->machStatePtr,
Sched_StartUserProc, (Address)NIL, TRUE);
if (status != SUCCESS) {
return(status);
}
/*
* Initialize all of the segments. The system segment is the standard one.
* The code segment is the same as the parent process. The stack segment
* is a copy of the parents, unless vforkFlag == TRUE in which case
* the ref count is boosted. Finally the heap segment is either a copy
* or the same as the parent depending on the share heap flag.
*/
procPtr->vmPtr->segPtrArray[VM_SYSTEM] = (Vm_Segment *) NIL;
if (vforkFlag) {
Vm_SegmentIncRef(parentProcPtr->vmPtr->segPtrArray[VM_STACK], procPtr);
procPtr->vmPtr->segPtrArray[VM_STACK] =
parentProcPtr->vmPtr->segPtrArray[VM_STACK];
} else {
status = Vm_SegmentDup(parentProcPtr->vmPtr->segPtrArray[VM_STACK],
procPtr, &(procPtr->vmPtr->segPtrArray[VM_STACK]));
if (status != SUCCESS) {
Mach_FreeState(procPtr);
return(status);
}
}
if (shareHeap || vforkFlag) {
Vm_SegmentIncRef(parentProcPtr->vmPtr->segPtrArray[VM_HEAP], procPtr);
procPtr->vmPtr->segPtrArray[VM_HEAP] =
parentProcPtr->vmPtr->segPtrArray[VM_HEAP];
} else {
status = Vm_SegmentDup(parentProcPtr->vmPtr->segPtrArray[VM_HEAP],
procPtr, &(procPtr->vmPtr->segPtrArray[VM_HEAP]));
if (status != SUCCESS) {
Vm_SegmentDelete(procPtr->vmPtr->segPtrArray[VM_STACK], procPtr);
Mach_FreeState(procPtr);
return(status);
}
}
if (parentProcPtr->vmPtr->sharedSegs != (List_Links *)NIL) {
Vm_CopySharedMem(parentProcPtr, procPtr);
}
Vm_SegmentIncRef(parentProcPtr->vmPtr->segPtrArray[VM_CODE], procPtr);
procPtr->vmPtr->segPtrArray[VM_CODE] =
parentProcPtr->vmPtr->segPtrArray[VM_CODE];
return(SUCCESS);
}